<?php
///////////////////////////////////////////////////////////////////////////////////////////////////////
//  这个文件是 JCAT PHP框架的一部，该项目和此文件 均遵循 GNU 自由软件协议
// 
//  Copyleft 2008 JeCat.cn(http://team.JeCat.cn)
//
//
//  JCAT PHP框架 的正式全名是：Jellicle Cat PHP Framework。
//  “Jellicle Cat”出自 Andrew Lloyd Webber的音乐剧《猫》（《Prologue:Jellicle Songs for Jellicle Cats》）。
//  JCAT 是一个开源项目，它像音乐剧中的猫一样自由，你可以毫无顾忌地使用JCAT PHP框架。
//  JCAT 由中国团队开发维护，是一个国际性的项目。
//  正在使用的这个版本是：0.3.2 / SVN信息：$Id: class.JCAT_Document.php 1250 2009-02-07 04:53:17Z aleechou $
//
//
//
//  相关的链接：
//    [主页] http://jcat.JeCat.cn
//    [HTTP下载] http://jcat.JeCat.cn/download
//    [在线文档] http://jcat.JeCat.cn/document
//    [社区] http://jj.jecat.cn/forum-7-1.html
//  不很相关：
//    [MP3] http://www.google.cn/search?q=jellicle+songs+for+jellicle+cats+Andrew+Lloyd+Webber
//    [VCD/DVD] http://www.google.cn/search?q=CAT+Andrew+Lloyd+Webber+video
//
///////////////////////////////////////////////////////////////////////////////////////////////////////
/*-- Project Introduce --*/




/**
 * What's this Class ?
 *
 * @author		alee
 * @access		public
 */
abstract class JCAT_Document extends JCAT_EventProcessor implements JCAT_IDocument
{

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$sName					string	What's this Parameter ?
	 * @param	$nStruct=self::DYNAMIC	int		动态 或 静态 结构文档
	 * @return	void
	 */
	public function JCAT_Document( $sName, $nStruct=self::STRUCT_DYNAMIC )
	{
		JCAT_ASSERT::ASSERT_STRING($sName) ;
		$this->SetName($sName) ;

		$this->_CommonConstruct() ;
		
		$this->nStruct = $nStruct ;
		
		// 注册为全局名称
		// self::RegisterGlobalName($this) ;	
	}

	/**
	 * 常规构造函数， 在 构造函数 和  __wakeup() 中 调用
	 * 
	 * @access	protected
	 * @return	void
	 */
	protected function _CommonConstruct()
	{
		$this->aViewContainer = new _JCAT_MVCObjectContainer('JCAT_IView') ;
		$this->aDocumentContainer = new _JCAT_MVCObjectContainer('JCAT_IDocument',true) ;	
	}

	/**
	 * 序列化 或  反序列化
	 * 
	 * @access	public
	 * @param	$nType=self:Save	int	序列化方向
	 * @return	void
	 */
	public function Serialize( $nType=JCAT_Document::Save )
	{
		// 保存
		if( $nType==JCAT_Document::Save )
		{
			// 保存
			if( $this->IsExists() )
			{
				return $this->Save() ;
			}
			
			// 新建
			else
			{
				return $this->Create() ;
			}
		}
		
		// 读取
		else if( $nType==JCAT_Document::Load )
		{
			// 读取操作 
			if( $this->Load() )
			{
				// 设置变化
				$this->SetChanged(false) ;
				
				return true ;
			}

			else 
			{
				return false ;
			}
		}
		
		// 意外的参数
		else
		{
			JCAT_ASSERT::ASSERT_(0,'!?');
		}
	}


	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$sKey	string	What's this Parameter ?
	 * @return	void
	 */
	public function __get( $sKey ) 
	{
		$sMsg=JCAT_Language::SentenceEx('正在访问未知的文档数据：“%s”','JCAT',null, $sKey) ;
		if( isset($this->arrDatas[$sKey]) )
			return $this->arrDatas[$sKey] ;
		
		else
			throw new JCAT_Exception( $sMsg, __macro_exception_code__ ) ;
	}

	public function __sleep()
	{
		$arrSavePropertys[] = JCAT_Package::MakePrivatePropertyNameForSerialize('arrDatas',__CLASS__) ;
		$arrSavePropertys[] = JCAT_Package::MakePrivatePropertyNameForSerialize('aDocumentContainer',__CLASS__) ;
		return $arrSavePropertys ;
	}

	public function __wakeup()
	{
		$this->_CommonConstruct() ;
	}
	
	function __clone()
    {
        $this->aDocumentContainer = clone $this->aDocumentContainer ;
        $this->aViewContainer = clone $this->aViewContainer ;
    }
	
	/**
	 * 检查文档数据 $sDataName 是否存在 
	 * 
	 * @access	public
	 * @param	$sDataName		string
	 * @return	bool
	 */
	public function IsDataExists($sDataName)
	{
		$arrDatas =& $this->arrDatas ;		// array_key_exists() 无法直接访问私有属性 $this->arrDatas 
		return array_key_exists($sDataName,$arrDatas) ;
	}

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$aDoc		JCAT_IDocument		What's this Parameter ?
	 * @param	$sName=''	string				What's this Parameter ?
	 * @static
	 * @return	old_value
	 */
	static public function RegisterGlobalName( JCAT_IDocument $aDoc, $sName='' ) 
	{
		JCAT_ASSERT::ASSERT_STRING($sName) ;
		if( $sName==='' )
			$sName = $aDoc->GetName() ;
			
		$old_value = isset(self::$arrGlobalDocuments[$sName])? self::$arrGlobalDocuments[$sName]: null ;
		self::$arrGlobalDocuments[$sName] = $aDoc ;
		return $old_value ;
	}

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$DocOrName		JCAT_IDocument,string		What's this Parameter ?
	 * @static
	 * @return	void
	 */
	static public function UnregisterGlobalName( $DocOrName ) 
	{
		JCAT_ASSERT::ASSERT_ISTHESE($DocOrName,array('JCAT_IDocument','string')) ;
		
		// 传入 视图名称
		if( is_string($DocOrName) )
		{
			if( !isset(self::$arrGlobalDocuments[$DocOrName]) )
				return null ;

			unset(self::$arrGlobalDocuments[$DocOrName]) ;
		}
		
		// 传入 视图对象
		else
		{
			// 删除 该对象 的所有 引用
			while( 1 )
			{
				$sName = array_search($DocOrName,self::$arrGlobalDocuments) ;
			
				if( in_array($sName,array(null,false),true) )
				{
					break ;
				}
				
				self::UnregisterGlobalName( strval($sName) ) ;
			}
		}
		
	}
	
	


	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$sName	string	What's this Parameter ?
	 * @static
	 * @return	JCAT_IDocument
	 */
	static public function GetDocumentByGlobalName( $sName ) 
	{
		JCAT_ASSERT::ASSERT_STRING($sName) ;
		return isset(self::$arrGlobalDocuments[$sName])? self::$arrGlobalDocuments[$sName]: null ;
	}

	
	
	///////// 实现 JCAT_IDocument 接口 ///////////////////////////////////////////////////////////////////////////////
	
	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	string
	 */
	public function GetName()
	{ return $this->sName ; }



	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$sName	string	What's this Parameter ?
	 * @return	old_var
	 */
	public function SetName( $sName )
	{
		JCAT_ASSERT::ASSERT_STRING($sName) ;
		
		$old_var = $this->sName ;
		$this->sName = $sName ;
		return $old_var ;
	}


	
	/**
	 * what's this
	 * 
	 * @access	protected
	 * @param	$sDataName								string		文档数据名称
	 * @param	$Value=self::NULL_DATA_VALUE			mixed		初始值
	 * @return	void
	 */
	protected function SetStructure($sDataName,$Value=self::NULL_DATA_VALUE)
	{
		JCAT_ASSERT::ASSERT_STRING($sDataName) ;
		$this->arrDatas[$sDataName] = $Value ;
		
		// 只能在 构造函数 中调用
		/*$aCaller = JCAT_Backtrace::GetCaller() ;
		$sMethodName = $aCaller->GetFunction() ;
		 
		if( $sMethodName=='' or $sMethodName==$aCaller->GetClass() )
		{
			$this->arrDatas[$sDataName] = $Value ;
		}
		
		else
		{
			JCAT_ASSERT::ASSERT_(false,JCAT_Language::SentenceEx('只能在构造函数中调用 JCAT_Document::SetStructure() 方法。','JCAT',null)) ;
		}*/
	}

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$sDataName	string	What's this Parameter ?
	 * @return	mixed, null
	 */
	public function Get( $sDataName )
	{
		JCAT_ASSERT::ASSERT_STRING($sDataName) ;
		return isset($this->arrDatas[$sDataName])? $this->arrDatas[$sDataName]: null ;
	}



	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$sDataName			string	What's this Parameter ?
	 * @param	$Value				mixed	What's this Parameter ?
	 * @param	$bSetChanged=true	bool	What's this Parameter ?
	 * @return	old_var
	 */
	public function Set( $sDataName, $Value, $bSetChanged=true )
	{
		JCAT_ASSERT::ASSERT_STRING($sDataName) ;

		if( $this->nStruct==self::STRUCT_STATIC )
		{
			JCAT_ASSERT::ASSERT_( $this->IsDataExists($sDataName)
				, JCAT_Language::SentenceEx('正在存储一个不存在的文档数据：“%s”。当前文档（%s）为静态结构，无法通过 JCAT_Document::SetStructure()以外的其他方法改变文档结构。','JCAT',null,$sDataName,$this->GetName())) ;
		}
		
		$old_var = isset($this->arrDatas[$sDataName])? $this->arrDatas[$sDataName]: null ;
		$this->arrDatas[$sDataName] = $Value ;
		
		if( $bSetChanged and $Value!==$old_var )
		{
			$this->SetChanged() ;
		}
			
		return $old_var ;
	}



	/**
	 * 返回所有数据 的 数组，仅提供给派生类
	 * 
	 * @access	protected
	 * @param 	$bExcludeNull=false	bool	排除为 null 的数据
	 * @return	array
	 */
	protected function GetDatas( $bExcludeNull=false )
	{
		if($bExcludeNull)
		{
			$arrRet = array() ;
			
			foreach ($this->arrDatas as $sDataName=>&$Data)
			{
				if($Data!==self::NULL_DATA_VALUE)
				{
					$arrRet[$sDataName] = $Data ;
				}
			}
			
			return $arrRet ;
		}
		
		else 
		{
			return $this->arrDatas ;
		}
	}

	/**
	 * 一次性设置所有数据，仅提供给派生类
	 * 
	 * @access	protected
	 * @param 	$arrDatas	array  数据数组
	 * @return	old_value
	 */
	protected function SetDatas( array $arrDatas )
	{
		$old_value = $this->arrDatas ;
		$this->arrDatas =& $arrDatas ;
		return $old_value ;
	}



	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$bQueryChildren=true	bool
	 * @return	bool
	 */
	public function IsChanged( $bQueryChildren=true ) 
	{
		// 查询自身
		if( $this->bChanged )
		{
			return true ;
		}
		
		// 查询子文档
		if( $bQueryChildren )
		{
			$aIterator = $this->CreateDocumentIterator() ;
			$aIterator->First() ;
			while( !$aIterator->IsDone() )
			{
				$aChild = $aIterator->Current() ;
				
				if( $aChild->IsChanged($bQueryChildren) )
				{
					return true ;
				}
				
				$aIterator->Next() ;
			}
			
		}
		
		return false ;
	}



	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$bChanged=true			bool	What's this Parameter ?
	 * @param	$bSetChildren=false		bool	What's this Parameter ?
	 * @return	old_var
	 */
	public function SetChanged( $bChanged=true, $bSetChildren=false )
	{
		$old_var = $this->IsChanged($bSetChildren) ;
		
		// 更新 自身
		$this->bChanged = (bool)$bChanged ;
		
		// 更新 子文档
		if( $bSetChildren )
		{
			$aIterator = $this->CreateDocumentIterator() ;
			$aIterator->First() ;
			while( !$aIterator->IsDone() )
			{
				$aChild = $aIterator->Current() ;
				
				$aChild->SetChanged($bChanged,$bSetChildren) ;
				
				$aIterator->Next() ;
			}
			
		}
		
		
		return $old_var ;
	}



	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$aBut=null	JCAT_IView	What's this Parameter ?
	 * @return	void
	 */
	public function UpdateAllView( JCAT_IView $aBut=null )
	{
		$aIterator = $this->CreateViewIterator() ;
		$aIterator->First() ;
		while( !$aIterator->IsDone() )
		{
			$aView = $aIterator->Current() ;
			
			if($aView!==$aBut)
			{
				// !! 因为 视图及其 子视图 可能 设置为相同的文档，
				// 文档负责 更新所有的 视图，
				// 视图 更新时 更新其子视图，则会造成 子视图的 重复更新。
				$aView->Update(false) ;
			}
			
			$aIterator->Next() ;
		}
	}



	/**
	 * 彻底 销毁一个文档 在  MVC 系统中的所有引用，以便在不需要该文档时能够立刻回收：
	 *  文档的全局名称上的引用
	 *  ... ...
	 * 
	 * @access	public
	 * @return	void
	 */
	public function Destroy()
	{
		// 清除 视图的全局名称
		self::UnregisterGlobalName($this) ;
	}
	
	
	///////// 实现 JCAT_IViewContainer 接口 ///////////////////////////////////////////////////////////////////////////////
	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$aView		JCAT_IView		What's this Parameter ?
	 * @param	$sName=null	string			What's this Parameter ?
	 * @return	void
	 */
	public function AddView( JCAT_IView $aView, $sName=null )
	{
		return $this->aViewContainer->Add($aView,$sName) ;
	}
	

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$sViewName	JCAT_IView		What's this Parameter ?
	 * @return	void
	 */
	public function RemoveView( JCAT_IView $aView )
	{ return $this->aViewContainer->Remve($aView) ; }


	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$sName	string	What's this Parameter ?
	 * @return	
	 */
	public function GetView( $sName )
	{ return $this->aViewContainer->Get($aView) ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	int
	 */
	public function GetViewCount(  ) 
	{ return $this->aViewContainer->GetObjectCount() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	int
	 */
	public function ClearView(  )
	{ return $this->aViewContainer->ClearObject() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	JCAT_ArrayIterator
	 */
	public function CreateViewIterator(  )
	{ return $this->aViewContainer->CreateObjectIterator() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	array
	 */
	public function CreateViewMemento(  )
	{ return $this->aViewContainer->CreateObjectMemento() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$arrMemento	array	What's this Parameter ?
	 * @return	void
	 */
	public function SetViewMemento( $arrMemento )
	{ return $this->aViewContainer->SetObjectMemento($arrMemento) ; }



	///////// 实现 JCAT_IDocumentContainer 接口 ///////////////////////////////////////////////////////////////////////////////
	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$aDocument		JCAT_IDocument	What's this Parameter ?
	 * @param	$sName=null		string			What's this Parameter ?
	 * @return	void
	 */
	public function AddDocument( JCAT_IDocument $aDocument, $sName=null )
	{
		return $this->aDocumentContainer->Add($aDocument,$sName) ;
	}

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$aDocument		JCAT_IDocument	What's this Parameter ?
	 * @return	void
	 */
	public function RemoveDocument( JCAT_IDocument $aDocument )
	{ return $this->aDocumentContainer->Remove($aDocument) ; }

	/**
	 * 取得一个子文档
	 * 
	 * @access	public
	 * @param	$sName	string	What's this Parameter ?
	 * @return	JCAT_Document
	 */
	public function GetDocument( $sName )
	{ return $this->aDocumentContainer->Get($sName) ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	int
	 */
	public function GetDocumentCount(  ) 
	{ return $this->aDocumentContainer->GetObjectCount() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	int
	 */
	public function ClearDocument(  )
	{ return $this->aDocumentContainer->ClearObject() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	JCAT_ArrayIterator
	 */
	public function CreateDocumentIterator(  )
	{ return $this->aDocumentContainer->CreateObjectIterator() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @return	array
	 */
	public function CreateDocumentMemento(  )
	{ return $this->aDocumentContainer->CreateObjectMemento() ; }

	/**
	 * What's this Method ?
	 * 
	 * @access	public
	 * @param	$arrMemento	array	What's this Parameter ?
	 * @return	void
	 */
	public function SetDocumentMemento( $arrMemento )
	{
		JCAT_ASSERT::ASSERT_ISTHESE($arrMemento,array('array:JCAT_IDocument')) ;
		return $this->aDocumentContainer->SetObjectMemento($arrMemento) ;
	}






	// 属性 ///////////////////////////////////////////////////////////////////////////////

	/**
	 * 动态结构
	 * 
	 * @access	const
	 * @var		int
	 */
	const STRUCT_DYNAMIC = 1 ;
	
	/**
	 * 静态结构
	 * 
	 * @access	const
	 * @var		int
	 */
	const STRUCT_STATIC = 2 ;
	
	/**
	 * 结构类型：静态/动态
	 * 
	 * @access	private
	 * @var		int
	 */
	private $nStruct ;

	/**
	 * What's this Attribute ?
	 * 
	 * @access	const
	 * @var		int
	 */
	const Save = 1 ;

	/**
	 * What's this Attribute ?
	 * 
	 * @access	const
	 * @var		int
	 */
	const Load = 2 ;

	/**
	 * What's this Attribute ?
	 * 
	 * @access	private
	 * @var		string
	 */
	private $sName ;

	/**
	 * What's this Attribute ?
	 * 
	 * @access	private
	 * @var		array
	 */
	private $arrDatas = array() ;

	/**
	 * What's this Attribute ?
	 * 
	 * @access	private
	 * @var		_JCAT_MVCObjectContainer
	 */
	private $aDocumentContainer ;

	/**
	 * What's this Attribute ?
	 * 
	 * @access	private
	 * @var		
	 */
	private $aViewContainer ;

	/**
	 * What's this Attribute ?
	 * 
	 * @access	private
	 * @var		bool
	 */
	private $bChanged = false ;


	/**
	 * What's this Attribute ?
	 * 
	 * @access	private
	 * @var		array
	 * @static
	 */
	static private $arrGlobalDocuments ;

	const NULL_DATA_VALUE = null ; 
}


?>